import os
import re
from typing import List, Dict, Any, Optional


class PerfCommandGenerator:
    """用于解析PCM事件文件并生成perf stat采集命令的类"""

    def __init__(self, file_path: str):
        """初始化解析器并加载PCM事件文件

        Args:
            file_path: PCM事件文件的路径
        """
        self.file_path = file_path
        self.event_groups: List[List[List[str]]] = [[], []]  # 按照参考格式组织事件组
        self._load_file()

    def _load_file(self) -> None:
        """加载并解析PCM事件文件"""
        if not os.path.exists(self.file_path):
            raise FileNotFoundError(f"文件不存在: {self.file_path}")

        current_group = []
        try:
            with open(self.file_path, "r", encoding="utf-8") as f:
                for line in f:
                    line = line.strip()
                    # 跳过空行、注释和非事件行
                    if not line or line.startswith("#") or line in ["(", ")", ";"]:
                        continue

                    # 将事件添加到当前组
                    current_group.append(line)
        except Exception as e:
            raise ValueError(f"解析文件时出错: {e}")

        # 按照参考格式组织事件组
        # 第0组存放一些基础事件
        # 第1组存放从文件解析的事件
        self.event_groups[0] = []  # 参考代码中第0组似乎是空的
        self.event_groups[1] = [
            current_group
        ]  # 将所有解析的事件放在第1组的第一个schema中

    def generate_cmd(self) -> str:
        """根据解析的事件组生成perf stat采集命令

        Returns:
            生成的perf stat命令字符串
        """
        # 基础命令，包含一些默认事件
        cmd = "perf stat -e instructions:D,cycles:D,ref-cycles:D,topdown.slots:D,"

        # 处理event_groups[1]中的事件
        if len(self.event_groups) > 1:
            for sche_idx, sche in enumerate(self.event_groups[1]):
                event_cmd = ""
                for event_idx, g in enumerate(sche):
                    event_cmd += g
                    if event_idx < len(sche) - 1:
                        event_cmd += ","

                # 将事件组用花括号括起来
                cmd += f'"{{{event_cmd}}}"'
                if sche_idx < len(self.event_groups[1]) - 1:
                    cmd += ","

        # 添加perf stat的其他参数
        cmd += ' -C 0 -a -x "," -I 1000 -- sleep 30 > result.csv 2>&1'

        return cmd

    def get_event_groups(self) -> List[List[List[str]]]:
        """获取解析后的事件组

        Returns:
            事件组的嵌套列表
        """
        return self.event_groups.copy()

    def filter_events(
        self,
        include_pattern: Optional[str] = None,
        exclude_pattern: Optional[str] = None,
    ) -> None:
        """根据正则表达式过滤事件

        Args:
            include_pattern: 包含的事件名称模式
            exclude_pattern: 排除的事件名称模式
        """
        if not self.event_groups or len(self.event_groups) <= 1:
            return

        filtered_groups = [[], []]
        filtered_groups[0] = self.event_groups[0]  # 保留第0组

        # 过滤第1组
        for sche in self.event_groups[1]:
            filtered_sche = []
            for event in sche:
                # 检查包含模式
                if include_pattern and not re.search(include_pattern, event):
                    continue
                # 检查排除模式
                if exclude_pattern and re.search(exclude_pattern, event):
                    continue
                # 如果通过了所有过滤条件，则保留该事件
                filtered_sche.append(event)

            if filtered_sche:  # 只有当过滤后的schema不为空时才添加
                filtered_groups[1].append(filtered_sche)

        self.event_groups = filtered_groups

    def save_to_file(self, output_path: str) -> None:
        """将生成的命令保存到文件

        Args:
            output_path: 输出文件路径
        """
        cmd = self.generate_cmd()
        with open(output_path, "w", encoding="utf-8") as f:
            f.write(cmd + "\n")

    def split_events_by_group(self, max_events_per_group: int = 16) -> None:
        """将事件分组，每组不超过指定数量的事件

        Args:
            max_events_per_group: 每组最大事件数
        """
        if not self.event_groups or len(self.event_groups) <= 1:
            return

        original_groups = self.event_groups[1]
        if not original_groups:
            return

        # 假设原始数据在第一个schema中
        all_events = original_groups[0] if original_groups else []
        new_schemas = []

        # 分组
        for i in range(0, len(all_events), max_events_per_group):
            new_schemas.append(all_events[i : i + max_events_per_group])

        # 更新事件组
        self.event_groups[1] = new_schemas


# 示例使用方法
if __name__ == "__main__":
    import sys

    if len(sys.argv) > 1:
        file_path = sys.argv[1]
    else:
        # 默认使用示例路径
        file_path = "/home/xyjiang/project/zenmpx/data/spr/sapphirerapids_server_events_nda_pcm.txt"

    try:
        # 创建解析器实例
        generator = PerfCommandGenerator(file_path)

        # 输出事件组信息
        event_groups = generator.get_event_groups()
        print(f"解析到 {len(event_groups[1])} 个事件组")
        total_events = sum(len(sche) for sche in event_groups[1])
        print(f"总事件数: {total_events}")

        # 根据需要可以过滤事件
        # generator.filter_events(include_pattern=r'UNC_CHA|UNC_M')

        # 可以将事件分成多个组（如果事件数量过多）
        # generator.split_events_by_group(max_events_per_group=10)

        # 生成并打印命令
        cmd = generator.generate_cmd()
        print(f"\n生成的perf stat命令:")
        print(cmd)

        # 可选：保存命令到文件
        # generator.save_to_file("perf_cmd.sh")

    except Exception as e:
        print(f"错误: {e}")
        sys.exit(1)
